
var lofi_char_image = new Image();
lofi_char_image.src = "lofi_char.png";

var STATE_WALKING = 1;
var STATE_FLYING = 2;  // mouse continues moving in a straight line,
                       // but it animates to look like it's jumping.
                       // ignores hazards/effects on the ground
                       // until state_duration = 0. Then set state = STATE_WALKING
                       // and state_duration = -1
var STATE_TELEPORTING_OUT = 3;  // mouse doesn't move. when state_duration
                                // reaches 0, set the mouse's coordinates
                                // to the grid space of the output teleporter,
                                // its state to = STATE_TELEPORTING_IN,
                                // and state_duration = TELEPORT_DURATION
var STATE_TELEPORTING_IN = 4;   // mouse doesn't move. When state_duration reaches
                                // 0, set the mouse's state to STATE_WALKING
                                // and state_duration = -1
var STATE_DEAD = 5;

var TELEPORT_DURATION = 30; // length in frames to wait on each side of teleport
var JUMP_DURATION = 60; // length in frames to be 'in the air'
var WALKING_SPEED = .05; // amount of ground covered, in tiles, per frame

var HEADING_NORTH = 0;
var HEADING_EAST = 1;
var HEADING_SOUTH = 2;
var HEADING_WEST = 3;

var NORTH_COORDS = [0, -1];
var EAST_COORDS = [1, 0];
var SOUTH_COORDS = [0, 1];
var WEST_COORDS = [-1, 0];

var FADE_PER_FRAME = 0.1;

var tile_width = 32;
var tile_height = 32;


function Mouse()
{
    this.x = -1;   // whole numbers only. This is the 'real' grid position of the mouse
    this.y = -1;   //
    this.last_x = -1;
    this.last_y = -1;
    this.progress = 0;

    this.heading = HEADING_NORTH;

    this.state = STATE_WALKING;
    this.state_duration = -1;  // Number of frames before it returns to STATE_WALKING. -1 means 'indefinite'

    if (Math.floor(Math.random() * 2) == 0) {
      this.gfx = { fg : lofi_char_image, fg_x : 0, fg_y : 384 };
    } else {
      this.gfx = { fg : lofi_char_image, fg_x : 32, fg_y : 384 };
    }

    // set this to true if you trigger a trap/switch/rotator whatever
    // so you don't trigger it again. set it to false every time you
    // change tiles.
    this.triggered = false;

    this.next = null;

    this.alpha = 1;
}

function getDirection(heading) {
    if (heading == HEADING_NORTH) {
        return [0, -1];
    } else if (heading == HEADING_EAST) {
        return [1, 0];
    } else if (heading == HEADING_SOUTH) {
        return [0, 1];
    } else if (heading == HEADING_WEST) {
        return [-1, 0];
    }
}

function drawMouse(mouse) {
    if (mouse.alpha > 0) {
        var canvas = document.getElementById("canvas");
        var c2d = canvas.getContext("2d");

        var gfx = mouse.gfx;
        var x = (1 - mouse.progress) * mouse.last_x +
                mouse.progress * mouse.x;
        var y = (1 - mouse.progress) * mouse.last_y +
                mouse.progress * mouse.y;

        c2d.globalAlpha = mouse.alpha;
        if (gfx.bg) {
            c2d.drawImage(gfx.bg,
                          gfx.bg_x, gfx.bg_y, tile_width, tile_height,
                          x * tile_width, y * tile_height, tile_width, tile_height);
        }

        if (gfx.fg) {
            c2d.drawImage(gfx.fg,
                          gfx.fg_x, gfx.fg_y, tile_width, tile_height,
                          x * tile_width, y * tile_height, tile_width, tile_height);
        }
        c2d.globalAlpha = 1;
    }
}

function updateMouseState(mouse) {
    var direction = getDirection(mouse.heading);
    if (mouse.state == STATE_WALKING) {
        // TODO: check and see if the mouse is on a hazard, or a wall, and kill it
        var tile = getTileTypeAtCoords(mouse.x, mouse.y);
        if (tile == 'w') {
            mouse.state = STATE_DEAD;
            dead_mice += 1;
            drawScoreboard();
            return;
        } else if (tile == 'S' && !mouse.triggered) {
            if (mouse.x + direction[0] * 2 < map_width &&
                mouse.x + direction[0] * 2 >= 0 &&
                mouse.y + direction[1] * 2 < map_height &&
                mouse.y + direction[1] * 2 >= 0) {
                mouse.x += direction[0] * 2;
                mouse.y += direction[1] * 2;
                return;
            }
            mouse.triggered = true;
        }
        var collision = checkCollisions(mouse);
        if (collision) {
            rotateMouse(mouse, 1);
        } else {
            mouse.progress += WALKING_SPEED;
            if (mouse.progress >= 1) {
              // Check last tile for interactions.
              var tile = getTileTypeAtCoords(mouse.x, mouse.y);
              if (tile == 'h') {
                  mouse.state = STATE_DEAD;
                  dead_mice += 1;
                  drawScoreboard();
                  return;
              } else if (tile == 'g') {
                  mouse.state = STATE_DEAD; // TODO: this seems a little inglorious
                  score += 1;
                  drawScoreboard();
                  if (score >= next_level) {
                    nextMap();
                  }
                  return;
              } else if (tile == 't' && teleporterOutExists()) {
                  // TODO: it would be funny to make them 'teleport nowhere' and die
                  //       if there is no output teleporter
                  mouse.state = STATE_TELEPORTING_OUT;
                  mouse.state_duration = TELEPORT_DURATION;
                  return;
              } else if (tile == 'r' && !mouse.triggered) {
                  rotateMouse(mouse, 3);
                  mouse.triggered = true;
                  return;
              } else if ((tile == 'C' || tile == 'c') && !mouse.triggered) {
                  flipSwitch();
                  mouse.triggered = true;
                  return;
              }
              mouse.last_x = mouse.x;
              mouse.last_y = mouse.y;
              mouse.x += direction[0];
              mouse.y += direction[1];
              mouse.progress = 0;
              mouse.triggered = false;
            }
        }
    }
    else if (mouse.state == STATE_FLYING) {
        mouse.state_duration -= 1;
        if (mouse.state_duration == 0) {
            mouse.state = STATE_WALKING;
            mouse.state_duration = -1;
        }
    } else if (mouse.state == STATE_TELEPORTING_OUT) {
        mouseTeleportingOut(mouse);
    } else if (mouse.state == STATE_TELEPORTING_IN) {
        mouseTeleportingIn(mouse);
    } else if (mouse.state == STATE_DEAD && mouse.alpha > 0) {
      mouse.alpha -= FADE_PER_FRAME;
    }
}


// This function does not really do a good job of keeping mice inside
// the playfield. Use walls to do that. Or fix this code.
// Returns true if the mouse hit something.
function checkCollisions(mouse) {
    if (mouse.heading == HEADING_NORTH) {
        // check the tile above the current tile for collisions
        if (!tiles[getTileTypeAtCoords(mouse.x, mouse.y - 1)].passable) {
            return true;
        }
    } else if (mouse.heading == HEADING_EAST) {
        // check the tile to the right for collisions
        if (!tiles[getTileTypeAtCoords(mouse.x + 1, mouse.y)].passable) {
            return true;
        }
    } else if (mouse.heading == HEADING_SOUTH) {
        // check the tile to the south for collisions
        if (!tiles[getTileTypeAtCoords(mouse.x, mouse.y + 1)].passable) {
            return true;
        }
    } else if (mouse.heading == HEADING_WEST) {
        // check the tile to the west for collisions
        if (!tiles[getTileTypeAtCoords(mouse.x - 1, mouse.y)].passable) {
            return true;
        }
    }
    return false;
}

function mouseTeleportingOut(mouse) {
    mouse.state_duration -= 1;
    if (mouse.state_duration == 0) {
        var coords = getTeleportCoordinates();
        if (coords[0] == -1 || coords[1] == -1) {
            mouse.state = STATE_DEAD;
            return;
        }
        mouse.x = coords[0];
        mouse.y = coords[1];
        mouse.state = STATE_TELEPORTING_IN;
        mouse.state_duration = TELEPORT_DURATION;
    }
}

function mouseTeleportingIn(mouse) {
    mouse.state_duration -= 1;
    if (mouse.state_duration == 0) {
        mouse.state = STATE_WALKING;
        mouse.state_duration = -1;
    }
}

function rotateMouse(mouse, amount) {
    mouse.heading = (mouse.heading + amount) % 4;
    if (checkCollisions(mouse)) {
        mouse.heading = (mouse.heading + amount * 2) % 4;
    }
}
